#include "core_pch.h"
#include "Core.h"
#include "Data.h"
#include "SString.h"

struct SStrData
{
	SStrData*	Next;
	u32_t		RefCount;
	char		Data[32];
};

enum
{
	Num_Name_Hash = 512
};
	
const s32_t CNameData_Header_Size = sizeof( SStrData*) + sizeof( u32_t) + sizeof(u16_t);

SStrData* __nameDatas[Num_Name_Hash];
s32_t __nameDataNum = 0;



void __incRefNameData( SStrData*& nameData)
{
	if( nameData == null_ptr)
	{
		return;
	}
	
	coreAtomicInc( nameData->RefCount);

}

void __decRefNameData( SStrData*& nameData)
{
	if( nameData == null_ptr)
	{
		return;
	}

	coreAtomicDec( nameData->RefCount);

	_check( nameData->RefCount >= 0);
	if (nameData->RefCount == 0)
	{
		u32_t hashId = _strHash( nameData->Data, Num_Name_Hash);
		SStrData** ptrPtrData = &__nameDatas[ hashId];
		while( (*ptrPtrData) != nameData)
		{
			ptrPtrData = &((*ptrPtrData)->Next);
		}

		(*ptrPtrData) = nameData->Next;

		--__nameDataNum;
		coreFree( nameData);
	}

	nameData = null_ptr;
}

// @@todo: mutex
SStrData* __allocateNameData(const char* srcStr)
{
    if ( srcStr == null_ptr)
        return null_ptr;

	u32_t hashId = _strHash( srcStr, Num_Name_Hash);

	SStrData** ptrPtrEnd = &__nameDatas[ hashId];

	while( *ptrPtrEnd)
	{
		if( strcmp( (*ptrPtrEnd)->Data, srcStr) == 0)
		{
			__incRefNameData( *ptrPtrEnd);
			return *ptrPtrEnd;
		}

		ptrPtrEnd = &((*ptrPtrEnd)->Next);
	}


    u32_t stLength = strlen( srcStr);
	u32_t bufSize = CNameData_Header_Size + stLength + 1;

	SStrData* rst = (SStrData*)coreAlloc( bufSize);
	if( rst != null_ptr)
	{
		(*ptrPtrEnd) = rst;
		rst->RefCount = 1;
		rst->Next = 0;
		coreMemcpy( rst->Data, srcStr, stLength);
		rst->Data[stLength] = '\0';
		++__nameDataNum;
	}

	return rst;
}



//////////////////////////SString//////////////////////////
SString::SString()
:mData( null_ptr)
{
}

SString::SString( const char* c_str)
{
	mData = __allocateNameData( c_str);
}

SString::SString( const SString& other)
:mData( other.mData)
{
	__incRefNameData( mData);
}

SString::~SString()
{
	__decRefNameData( mData);
}





SString& SString::operator=(const SString& other)
{
	__decRefNameData( mData);
	mData = other.mData;
	__incRefNameData( mData);
	return *this;
}

SString& SString::operator=(const char* c_str)
{
	__decRefNameData( mData);
	mData = __allocateNameData( c_str);
	return *this;
}


SString::operator const char*() const
{
	if( !mData)
	{
		return 0;
	}
	return mData->Data;
}

bool SString::Equals(const char* pcStr) const
{
	if( mData == null_ptr)
	{
		if( pcStr == null_ptr)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		if( pcStr == null_ptr)
		{
			return false;
		}

		if( mData->Data == pcStr)
		{
			return true;
		}

		return strcmp(mData->Data, pcStr) == false;

	}
}




bool operator==(const SString& s1, const SString& s2)
{
	return s1.mData == s2.mData;
}
	
bool operator==(const SString& s1, const char* s2)
{
	return s1.Equals(s2);
}
	
bool operator==(const char* s1, const SString& s2)
{
	return s2.Equals(s1);
}
	
bool operator==(const SString& s1, const DString& s2)
{
	return s2 == (const char*)s1;
}
	
bool operator==(const DString& s1, const SString& s2)
{
	return s1 == (const char*)s2;
}

	
bool operator!=(const SString& s1, const SString& s2)
{
	return s1.mData != s2.mData;
}
	
bool operator!=(const SString& s1, const char* s2)
{
	return !s1.Equals(s2);
}

	
bool operator!=(const char* s1, const SString& s2)
{
	return !s2.Equals(s1);
}
	
bool operator!=(const SString& s1, const DString& s2)
{
	return s2 != (const char*)s1;
}
	
bool operator!=(const DString& s1, const SString& s2)
{
	return s1 != (const char*)s2;
}



void SString::StaticInit()
{
	coreMemzero( __nameDatas, sizeof( __nameDatas));
}

void SString::StaticShutdown()
{
	_check( __nameDataNum == 0);
}
